#include "dag.h"
#include "dag_graph_operator.h"
#include "dag_teach.h"

#include <set>
#include <map>
#include <vector>
#include <algorithm>

using namespace dag;
using namespace std;


GraphNode::GraphNode() {
}

GraphNode::~GraphNode() throw() {
}

const Graph* GraphNode::getGraph() const {
	return m_Graph.get();
}

void GraphNode::setGraph(const Graph& graph) {
	m_Graph.reset(new Graph(graph));
	m_NodeTraits = get_node_set_traits(m_Graph->getNodes());
}

const NodeTraits& GraphNode::traits() const {
	return	m_NodeTraits;
}


void GraphNode::_init(const FrameSpec& expectedInputSpec) {
	if (m_Graph.get() == NULL) {
		throw NodeStateError();
	}
	
	m_InputSpec = expectedInputSpec;

	list<GraphError> errors;
	initialize(*m_Graph, m_InputSpec, m_OutputSpec, errors);
	
	if (errors.size() > 0) {
		m_InputSpec = FrameSpec();
		m_OutputSpec = FrameSpec();
		/* if there was an error during initialization, throw the first error */
		throw errors.front();
	}
}

const FrameSpec& GraphNode::_inputSpec() const {
	return m_InputSpec;
}

const FrameSpec& GraphNode::_outputSpec() const {
	return m_OutputSpec;
}

auto_ptr<FrameOperator> GraphNode::_createForwardOp() const {
	if (m_Graph.get() == NULL) {
		throw NodeStateError();
	}
	return auto_ptr<FrameOperator>(new GraphOperator(*this, false));
}

auto_ptr<FrameOperator> GraphNode::_createReverseOp() const {
	if (m_Graph.get() == NULL) {
		throw NodeStateError();
	}
	return auto_ptr<FrameOperator>(new GraphOperator(*this, true));
}

void GraphNode::_learn(const FrameDepot* input, const FrameDepot* output) {
	if (m_Graph.get() == NULL) {
		throw NodeStateError();
	}

	/* validate inputs */
	if (m_NodeTraits.resources == LearnResource_Inputs ||
		m_NodeTraits.resources == LearnResource_InputsOutputs) {
		/* check to see that frame spec matches that used during call to init */
		if (input->spec() != m_InputSpec) {
			throw InvalidArgument();
		}
	
	}
	
	if (m_NodeTraits.resources == LearnResource_Outputs ||
		m_NodeTraits.resources == LearnResource_InputsOutputs) {
		/* check to see that frame spec matches that used during call to init */
		if (output->spec() != m_OutputSpec) {
			throw InvalidArgument();
		}
	}
	
	/* need to make sure we have a complete and properly created graph */
	updateLearnProgress("validating graph links", 0.0);
	list<GraphError> errors;
	validate_links(*m_Graph, m_InputSpec, m_OutputSpec, errors);
	updateLearnProgress("validating graph resource access", 0.01);
	validate_resources(*m_Graph, errors);
	
	if (errors.size() > 0) {
		throw errors.front();
	}
	
	/* make sure there are no loops in the graph */
	list<set<const Node*> > loops;
	updateLearnProgress("ensuring no loops exist in graph", 0.03);
	find_loops(*m_Graph, loops);
	if (loops.size() > 0) {
		throw LoopError(loops.front());
	}
	
	updateLearnProgress("teaching sub nodes", 0.04);
	dag::teach_graph(*m_Graph, input, output);
	updateLearnProgress("complete", 1.0);
}



